/*
Autor: Alexander Savochkin, 2010
New BSD License
*/

#pragma once

#include <string>
#include <map>
#include <boost/weak_ptr.hpp>
#include <boost/shared_ptr.hpp>

using std::string;
using std::map;
using boost::shared_ptr;
using boost::weak_ptr;

#include "IOperatedInput.h"
#include "IOperatedOutput.h"
#include "Config.h"

namespace SRCLF 
{

using Config::Configuration;

namespace IO
{

	template<typename SharedResourceT>
	class SharedResourceKeeper	//NB! shared_resources map doesn't clean automatically when shared resource destoyed. So this class works well only for fixed number of shared resources
	{	
		map<string, boost::weak_ptr<SharedResourceT> > shared_resources;	

		SharedResourceKeeper() {}
		~SharedResourceKeeper() {}
	public:
		static SharedResourceKeeper<SharedResourceT>* getInstance()
		{
			static SharedResourceKeeper<SharedResourceT> instance;
			return &instance;
		}

		shared_ptr<SharedResourceT> get(const string& resource_id, const Configuration& config)
		{
			shared_ptr<SharedResourceT> result;
			if(shared_resources.count(resource_id) == 0 || shared_resources[resource_id].expired())
			{
				shared_ptr<SharedResourceT> t(new SharedResourceT(resource_id, config) );
				shared_resources[resource_id] = t;
				result = t;
			}
			else
			{
				shared_ptr<SharedResourceT> t(shared_resources[resource_id]);
				result = t;
			}
			return result;
		}
	};

	template<typename SharedResourceT>
	class SharedResourceInput : public IOperatedInput
	{
		string resource_id;
		const Configuration& config;
	protected:
		shared_ptr<SharedResourceT> io_resource;
	public:
		SharedResourceInput(const string& _resource_id, const Configuration& _config) :
		  resource_id(_resource_id), config(_config) {}
		~SharedResourceInput() 
		{
			try {
				SharedResourceInput::close();	//Non-virtual call 
			}
			catch(...) {
				//TODO... Write to log
			}
		}

		bool open() 
		{
			io_resource = SharedResourceKeeper<SharedResourceT>::getInstance()->get(resource_id, config);
			return true;
		};

		void close() 
		{
			if (io_resource)
				io_resource.reset();
		};
	};

	template<typename SharedResourceT>
	class SharedResourceOutput : public IOperatedOutput
	{
		string resource_id;
		const Configuration& config;
	protected:
		shared_ptr<SharedResourceT> io_resource;
	public:
		SharedResourceOutput(const string& _resource_id, const Configuration& _config) :
		  resource_id(_resource_id), config(_config) {}
		~SharedResourceOutput() 
		{
			SharedResourceOutput::close();	//Non-virtual call 
		}

		virtual bool open() 
		{
			io_resource = SharedResourceKeeper<SharedResourceT>::getInstance()->get(resource_id, config);
			return true;
		};

		virtual void close() 
		{
			if (io_resource)
				io_resource.reset();
		};
	};

} //namespace IO
} //namespace SRCLF
